package com.tydic.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.tydic.common.core.enums.UnwantedAuthSignEnum;
import com.tydic.common.core.enums.UserCertificationStatusEnum;
import com.tydic.common.core.exception.api.ApiException;
import com.tydic.common.core.exception.api.FddException;
import com.tydic.common.core.utils.file.PdfUtils;
import com.tydic.system.domain.*;
import com.tydic.system.domain.req.*;
import com.tydic.system.domain.resp.*;
import com.tydic.system.service.IFddService;
import com.tydic.system.service.ISysOssService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
@RequiredArgsConstructor
public class FddServiceImpl implements IFddService {
    private final StringRedisTemplate stringRedisTemplate;
    private final ISysOssService sysOssService;
    @Value("${fdd.appId}")
    private String appId;
    @Value("${fdd.appKey}")
    private String appKey;
    @Value("${fdd.appId-nonautomatic}")
    private String appIdNonautomatic;
    @Value("${fdd.appKey-nonautomatic}")
    private String appKeyNonautomatic;
    @Value("${fdd.baseUrl}")
    private String baseUrl;
    @Value("${fdd.notifyUrlPrefix}")
    private String notifyUrlPrefix;

    private String getTokenRedisKey() {
        return "FDD_ACCESS_TOKEN";
    }

    @Override
    public String getToken() {
//        String token = stringRedisTemplate.opsForValue().get(getTokenRedisKey());
//        if (StrUtil.isNotBlank(token)) {
//            return token;
//        }
        String token = null;
        HttpResponse response = null;
        try {
            String requestUrl = String.format("%s/base/login/oauth2/token?appId=%s&appKey=%s", baseUrl, appId, appKey);
            response = HttpUtil.createGet(requestUrl).execute();
            JSONObject data = JSONObject.parseObject(response.body()).getJSONObject("data");
            token = data.getString("accessToken");
            int expireIn = data.getIntValue("expiresIn");
            stringRedisTemplate.opsForValue().set(getTokenRedisKey(), token, Duration.ofSeconds(expireIn));
        } catch (Exception e) {
            log.error("请求异常", e);
        } finally {
            IoUtil.close(response);
        }
        return token;
    }

    @Override
    public Account getAccountByMobile(String mobile) {
        Map<String, String> params = new HashMap<>();
        params.put("mobile", mobile);
        List<Account> accounts = doGet("/user/api/account/getAccount", params, new AccountListTypeReference());
        if (CollUtil.isEmpty(accounts)) {
            return null;
        }
        return accounts.get(0);
    }

    @Override
    public Account getAccountById(String accountId) {
        Map<String, String> params = new HashMap<>();
        params.put("accountId", accountId);
        List<Account> accounts = doGet("/user/api/account/getAccount", params, new AccountListTypeReference());
        if (CollUtil.isEmpty(accounts)) {
            return null;
        }
        return accounts.get(0);
    }

    @Override
    public String createAccount(Account account) {
        String json = JSON.toJSONString(account);
        Account result = doPostForJson("/user/api/account/createAccount", json, new AccountTypeReference());
        if (result == null) {
            return null;
        }
        return result.getAccountId();
    }

    @Override
    public String verifyAccount(String name, String mobile, String idCard, String personalBankCard, String returnUrl) {
        Account account = getAccountByMobile(mobile);
        if (account == null || StrUtil.equalsAny(account.getVerifyStatus(), UserCertificationStatusEnum.SUCCESS.getStatus())) {
            return null;
        }
        VerifyAccountRequest verifyAccountRequest = new VerifyAccountRequest();
        verifyAccountRequest.setAccountId(account.getAccountId());
        verifyAccountRequest.setName(name);
        if (StrUtil.isNotBlank(idCard)) {
            verifyAccountRequest.setIdCard(idCard);
        }
//        if (StrUtil.isNotBlank(personalBankCard)) {
//            verifyAccountRequest.setPersonalBankCard(personalBankCard);
//        }
        verifyAccountRequest.setNotifyUrl(notifyUrlPrefix + "/verifyAccount");
        verifyAccountRequest.setReturnUrl(returnUrl);
        String json = JSON.toJSONString(verifyAccountRequest);
        VerifyAccountResponse verifyAccountResponse = doPostForJson("/user/api/verify/person/url", json, new VerifyAccountResponseReference());
        if (verifyAccountResponse == null) {
            return null;
        }
        return verifyAccountResponse.getUrl();
    }

    @Override
    public String verifyChangeAccount(String name, String mobile, String idCard, String personalBankCard, String returnUrl) {
        Account account = getAccountByMobile(mobile);
        if (account == null) {
            return null;
        }
        VerifyChangeAccountRequest verifyChangeAccountRequest = new VerifyChangeAccountRequest();
        verifyChangeAccountRequest.setAccountId(account.getAccountId());
        verifyChangeAccountRequest.setName(name);
        if (StrUtil.isNotBlank(idCard)) {
            verifyChangeAccountRequest.setIdCard(idCard);
        }
//        if (StrUtil.isNotBlank(personalBankCard)) {
//            verifyChangeAccountRequest.setPersonalBankCard(personalBankCard);
//        }
        verifyChangeAccountRequest.setNotifyUrl(notifyUrlPrefix + "/verifyAccount");
        verifyChangeAccountRequest.setReturnUrl(returnUrl);
        String json = JSON.toJSONString(verifyChangeAccountRequest);
        VerifyAccountResponse verifyAccountResponse = doPostForJson("/user/api/verify/person/change", json, new VerifyAccountResponseReference());
        if (verifyAccountResponse == null) {
            return null;
        }
        return verifyAccountResponse.getUrl();
    }

    @Override
    public Company getCompanyByCompanyName(String companyName) {
        Map<String, String> params = new HashMap<>();
        params.put("companyName", companyName);
        List<Company> companies = doGet("/user/api/company/getCompany", params, new CompanyListTypeReference());
        if (CollUtil.isEmpty(companies)) {
            return null;
        }
        return companies.get(0);
    }

    @Override
    public Company getCompanyByCompanyId(String companyId) {
        Map<String, String> params = new HashMap<>();
        params.put("companyId", companyId);
        List<Company> companies = doGet("/user/api/company/getCompany", params, new CompanyListTypeReference());
        if (CollUtil.isEmpty(companies)) {
            return null;
        }
        return companies.get(0);
    }

    @Override
    public String createCompany(Company company) {
        String json = JSON.toJSONString(company);
        Company result = doPostForJson("/user/api/company/createCompany", json, new CompanyTypeReference());
        if (result == null) {
            return null;
        }
        return result.getCompanyId();
    }

    @Override
    public String verifyCompany(String mobile, String companyName, String creditCode, String bankName, String bankNumber, String branchName, String returnUrl) {
        Account account = getAccountByMobile(mobile);
        Company company = getCompanyByCompanyName(companyName);
        VerifyCompanyRequest verifyCompanyRequest = new VerifyCompanyRequest();
        VerifyCompanyInfoRequest verifyCompanyInfoRequest = new VerifyCompanyInfoRequest();
        VerifyCompanyBankCardRequest verifyCompanyBankCardRequest = new VerifyCompanyBankCardRequest();
        verifyCompanyInfoRequest.setCompanyName(companyName);
        verifyCompanyInfoRequest.setCreditCode(creditCode);
        verifyCompanyRequest.setCompanyInfoDTO(verifyCompanyInfoRequest);
        verifyCompanyBankCardRequest.setBankName(bankName);
        verifyCompanyBankCardRequest.setBranchName(branchName);
        verifyCompanyBankCardRequest.setBankNumber(bankNumber);
        verifyCompanyRequest.setBankCardDTO(verifyCompanyBankCardRequest);
        verifyCompanyRequest.setCompanyId(company.getCompanyId());
        verifyCompanyRequest.setAccountId(account.getAccountId());
        verifyCompanyRequest.setNotifyUrl(notifyUrlPrefix + "/verifyEnterprise");
        verifyCompanyRequest.setReturnUrl(returnUrl);
        String json = JSON.toJSONString(verifyCompanyRequest);
        VerifyCompanyResponse verifyCompanyResponse = doPostForJson("/user/api/verify/company/url", json, new VerifyCompanyResponseReference());
        if (verifyCompanyResponse == null) {
            return null;
        }
        return verifyCompanyResponse.getUrl();
    }

    @Override
    public String verifyChangeCompany(String mobile, String companyName, String creditCode, String bankName, String bankNumber, String branchName, String returnUrl) {
        Account account = getAccountByMobile(mobile);
        Company company = getCompanyByCompanyName(companyName);
        VerifyChangeCompanyRequest verifyChangeCompanyRequest = new VerifyChangeCompanyRequest();
        VerifyCompanyBankCardRequest verifyCompanyBankCardRequest = new VerifyCompanyBankCardRequest();
        verifyChangeCompanyRequest.setCompanyName(companyName);
        verifyCompanyBankCardRequest.setBankName(bankName);
        verifyCompanyBankCardRequest.setBranchName(branchName);
        verifyCompanyBankCardRequest.setBankNumber(bankNumber);
        verifyChangeCompanyRequest.setBankCardDTO(verifyCompanyBankCardRequest);
        verifyChangeCompanyRequest.setCompanyId(company.getCompanyId());
        verifyChangeCompanyRequest.setAccountId(account.getAccountId());
        verifyChangeCompanyRequest.setNotifyUrl(notifyUrlPrefix + "/verifyEnterprise");
        verifyChangeCompanyRequest.setReturnUrl(returnUrl);
        String json = JSON.toJSONString(verifyChangeCompanyRequest);
        VerifyCompanyResponse verifyCompanyResponse = doPostForJson("/user/api/verify/company/change", json, new VerifyCompanyResponseReference());
        if (verifyCompanyResponse == null) {
            return null;
        }
        return verifyCompanyResponse.getUrl();
    }


    /**
     * 合同相关文件上传
     *
     * @param request
     * @return
     */
    @Override
    public String upload(FileUploadRequest request) {
        Map<String, Object> map = new HashMap<>();
        map.put("fileName", request.getFileName());

        FileUploadResponse fileUploadResponse = doPostForForm("/base/api/file/upload",
            map,
            request.getFile(),
            new FileUploadResponseReference());
        if (fileUploadResponse != null) {
            return fileUploadResponse.getFileId();
        } else {
            throw new ApiException("上传合同失败，请稍后重试");
        }
    }

    /**
     * 创建并推送签署
     *
     * @param request
     * @return
     */
    @Override
    public CreateAndPushResponse createAndPush(CreateAndPushRequest request, String name) {
        request.setNotifyUrl(notifyUrlPrefix + name);
        request.setNotifyUrl(notifyUrlPrefix + name);
//        request.setNotifyUrl("http://81.69.46.155:8443/ca/index");
//        String jsonString = JSON.toJSONString(request);
        CreateAndPushResponse createAndPushResponse = doPostForJson("/base/api/contract/sign/createAndPush",
            JSON.toJSONString(request),
            new FileCreateAndPushResponseReference());
        if (createAndPushResponse != null) {
            return createAndPushResponse;
        } else {
            throw new ApiException("创建并推送签署失败");
        }

    }

    /**
     * 获取文档签署链接
     *
     * @param request
     * @return
     */
    @Override
    public String sign(FileSignRequest request) {
        FileSignResponse fileSignResponse = doPostForJson("/base/api/contract/manual/sign",
            JSON.toJSONString(request),
            new FileSignResponseReference());
        if (fileSignResponse != null) {
            return fileSignResponse.getUrl();
        } else {
            throw new ApiException("获取文档签署链接失败");
        }
    }

    /**
     * 获取文档签署链接（重新获取）
     *
     * @param request
     * @return
     */
    @Override
    public String getSignUrl(FileGetSignRepeatRequest request) {
        FileSignResponse fileSignResponse = doPostForJson("/base/api/contract/manual/getSignUrl",
            JSON.toJSONString(request),
            new FileSignResponseReference());
        if (fileSignResponse != null) {
            return fileSignResponse.getUrl();
        } else {
            throw new ApiException("重新获取文档签署链接失败");
        }
    }

    /**
     * 关闭签署任务
     *
     * @param request
     * @return
     */
    public void shutSignTask(ShutSignTaskRequest request) {
        doPostForJson("/base/api/contract/shutSignTask",
            JSON.toJSONString(request),
            new ShutSignTaskReference());
    }

    /**
     * 获取文档查看链接|获取文档内容查看地址
     *
     * @param request
     */
    public String pageView(PageViewRequest request) {
        return doPostForJson("/base/api/contract/pageView",
            JSON.toJSONString(request),
            new PageViewReference());

    }


    /**
     * 文档下载
     *
     * @param request
     * @return
     */
    @Override
    public DocDownloadResponse download(DocDownloadRequest request) {
        DocDownloadResponse docDownloadResponse = doPostForJson("/base/api/contract/getDocByContractId",
            JSON.toJSONString(request),
            new DownloadReference());
        if (docDownloadResponse != null) {
            return docDownloadResponse;
        } else {
            throw new ApiException("文档下载失败");
        }

    }

    /**
     * 开通免验证签服务
     *
     * @param request
     * @return
     */
    @Override
    public UnwantedAuthSignResponse openUnwantedAuthSign(UnwantedAuthSignRequest request) {
        request.setNotifyUrl(notifyUrlPrefix + "/openUnwantedAuthSignCallback");
        UnwantedAuthSignResponse unwantedAuthSignResponse = doPostForJson("/base/api/unwantedAuthSign/openUnwantedAuthSign",
            JSON.toJSONString(request),
            new UnwantedAuthReference());
        if (unwantedAuthSignResponse != null) {
            return unwantedAuthSignResponse;
        } else {
            throw new ApiException("开通免验证签服务失败");
        }
    }

    /**
     * 查询免验证签授权状态
     *
     * @param request
     * @return
     */
    @Override
    public UnwantedAuthSignStatusResponse searchUnwantedAuthSignStatus(SeachUnwantedAuthSignRequest request) {
        List<UnwantedAuthSignStatusResponse> response = doPostForJson("/base/api/unwantedAuthSign/searchUnwantedAuthSignStatus",
            JSON.toJSONString(request),
            new UnwantedAuthStatusReference());
        if (response != null) {
            return response.get(0);
        } else {
            throw new ApiException("查询免验证签授权状态失败");
        }
    }

    @Override
    public FinishUnwantedAuthSignResponse finishUnwantedAuthSign(FinishUnwantedAuthSignRequest request) {
        FinishUnwantedAuthSignResponse response = doPostForJson("/base/api/unwantedAuthSign/finishUnwantedAuthSign",
            JSON.toJSONString(request),
            new FinishUnwantedAuthSignReference());
        return response;
    }

    @Override
    public CloseUnwantedAuthSignResponse closeUnwantedAuthSign(CloseUnwantedAuthSignRequest request) {
        CloseUnwantedAuthSignResponse response = doPostForJson("/base/api/unwantedAuthSign/closeUnwantedAuthSign",
            JSON.toJSONString(request),
            new CloseUnwantedAuthSignReference());
        return response;
    }


    private <T> T doGet(String api, Map<String, String> params, TypeReference<FddResultResp<T>> typeReference) {
        HttpResponse response = null;
        try {
            StringBuilder requestUrl = new StringBuilder();
            requestUrl.append(baseUrl).append(api).append("?");
            if (CollUtil.isNotEmpty(params)) {
                for (Map.Entry<String, String> param : params.entrySet()) {
                    requestUrl.append(param.getKey()).append("=").append(param.getValue()).append("&");
                }
            }
            String url = requestUrl.substring(0, requestUrl.length() - 1);
            response = HttpUtil.createGet(url).header("Authorization", "bearer " + getToken()).execute();
            String json = response.body();
            FddResultResp<T> result = JSON.parseObject(json, typeReference);
            if (result.getCode() != 0) {
                throw new FddException(api, result.getMessage());
            }
            return result.getData();
        } catch (Exception e) {
            log.error("请求法大大接口异常", e);
        } finally {
            IoUtil.close(response);
        }
        return null;
    }


    private <T> T doPostForForm(String api, Map<String, Object> params, File file, TypeReference<FddResultResp<T>> typeReference) {
        HttpResponse response = null;
        try {

            String url = baseUrl + api;
            HttpRequest post = HttpUtil.createPost(url)
                .header("Authorization", "bearer " + getToken())
                .contentType("multipart/form-data");

            if (FileUtil.exist(file)) {
                post.form("file", FileUtil.readBytes(file), file.getName() + ".docx");
            }

            for (String key : params.keySet()) {
                post.form(key, params.get(key));
            }

            response = post.execute();
            String json = response.body();
            FddResultResp<T> result = JSON.parseObject(json, typeReference);
            if (result.getCode() != 0) {
                throw new FddException(api, result.getMessage());
            }
            return result.getData();
        } catch (Exception e) {
            log.error("请求法大大接口异常", e);
        } finally {
            IoUtil.close(response);
        }
        return null;
    }

    private <T> T doPostForJson(String api, String params, TypeReference<FddResultResp<T>> typeReference) {
        HttpResponse response = null;
        try {
            String url = baseUrl + api;
            HttpRequest post = HttpUtil.createPost(url).header("Authorization", "bearer " + getToken());
            post.body(params, "application/json");
            response = post.execute();
            String json = response.body();
            log.info("法大大响应结果:{}", json);
            FddResultResp<T> result = JSON.parseObject(json, typeReference);
            if (result.getCode() != 0) {
                if (result.getCode() == 24129) {
                    ArrayList<UnwantedAuthSignStatusResponse> list = new ArrayList<>();
                    UnwantedAuthSignStatusResponse unwantedAuthSignStatusResponse = new UnwantedAuthSignStatusResponse();
                    unwantedAuthSignStatusResponse.setStatus(UnwantedAuthSignEnum.UN_APPLY.getType());
                    list.add(unwantedAuthSignStatusResponse);
                    result.setData((T) list);
                    return result.getData();
                }
                throw new FddException(api, result.getMessage());
            }
            return result.getData();
        } catch (FddException e1) {
            throw e1;
        } catch (Exception e) {
            log.error("请求法大大接口异常", e);
            throw new ApiException("请求法大大接口异常");
        } finally {
            IoUtil.close(response);
        }
    }

    @Override
    public String uploadPfd(String docId) {
        DocDownloadRequest docDownloadRequest = new DocDownloadRequest();
        docDownloadRequest.setDocId(docId);
        DocDownloadResponse download = download(docDownloadRequest);
        if (download == null) {
            return null;
        }
        byte[] data = PdfUtils.toBytes(download.getFileUrl());
        String fileName = IdUtil.fastSimpleUUID() + ".pdf";
        String contentType = "application/pdf";
        return String.valueOf(sysOssService.upload(data, fileName, contentType).getOssId());
    }

    @Override
    public String uploadImage(String docId) {
        DocDownloadRequest docDownloadRequest = new DocDownloadRequest();
        docDownloadRequest.setDocId(docId);
        DocDownloadResponse download = download(docDownloadRequest);
        if (download == null) {
            return null;
        }
        byte[] data = PdfUtils.convertToImages(download.getFileUrl());
        String fileName = IdUtil.fastSimpleUUID() + ".png";
        String contentType = "image/png";
        return sysOssService.upload(data, fileName, contentType).getUrl();
    }

    private class AccountListTypeReference extends TypeReference<FddResultResp<List<Account>>> {

    }

    private class AccountTypeReference extends TypeReference<FddResultResp<Account>> {

    }

    private class CompanyListTypeReference extends TypeReference<FddResultResp<List<Company>>> {

    }

    private class CompanyTypeReference extends TypeReference<FddResultResp<Company>> {

    }

    private class VerifyAccountResponseReference extends TypeReference<FddResultResp<VerifyAccountResponse>> {

    }

    private class VerifyCompanyResponseReference extends TypeReference<FddResultResp<VerifyCompanyResponse>> {

    }

    private class FileUploadResponseReference extends TypeReference<FddResultResp<FileUploadResponse>> {

    }

    private class FileCreateAndPushResponseReference extends TypeReference<FddResultResp<CreateAndPushResponse>> {

    }

    private class FileSignResponseReference extends TypeReference<FddResultResp<FileSignResponse>> {

    }

    private class ShutSignTaskReference extends TypeReference<FddResultResp<ShutSignTaskResponse>> {

    }

    private class DownloadReference extends TypeReference<FddResultResp<DocDownloadResponse>> {

    }

    private class PageViewReference extends TypeReference<FddResultResp<String>> {

    }

    private class UnwantedAuthReference extends TypeReference<FddResultResp<UnwantedAuthSignResponse>> {

    }

    private class UnwantedAuthStatusReference extends TypeReference<FddResultResp<List<UnwantedAuthSignStatusResponse>>> {

    }

    private class FinishUnwantedAuthSignReference extends TypeReference<FddResultResp<FinishUnwantedAuthSignResponse>> {

    }

    private class CloseUnwantedAuthSignReference extends TypeReference<FddResultResp<CloseUnwantedAuthSignResponse>> {

    }
}
